import { Component, error, instantiate, Node, Prefab, sp, SpriteFrame, _decorator } from 'cc';
import { app } from '../App';
import { Logger } from '../common/log/Logger';
import { injectable, InjectableObject } from '../inject';
import UIDelegateComp from './component/UIDelegateComp';
import TipsLayer from './TipsLayer';
import UILayer from './UILayer';
import { UICallbacks, ViewConfig, ViewParams, ViewType } from './ViewDefine';
const { ccclass, property } = _decorator;


@injectable("UIManager")
export default class UIManager extends InjectableObject {

    private _root: Node = null;
    private _loading: Node = null;

    private _viewConfMap: Map<number, ViewConfig> = new Map();

    private ui!: UILayer;
    private tips!: TipsLayer;

    init(root: Node, loading: Node) {
        this._root = root;
        this._loading = loading;

        this.ui = this.injector.instantiateOf(UILayer, "UILayer");
        this._root.addChild(this.ui);

        this.tips = this.injector.instantiateOf(TipsLayer, "TipsLayer");
        this._root.addChild(this.tips);
    }

    public addConfig(id: number, config: ViewConfig): void {
        this._viewConfMap.set(id, config);
    }

    public addConfigs(configs: Map<number, ViewConfig>) {
        for (let [key, value] of configs) {
            this._viewConfMap.set(key, value);
        }
    }

    public toast(content: string) {
        if (this.tips)
            this.tips.show(content)
    }

    public showLoading() {
        this._loading.active = true;
    }

    public hideLoading() {
        this._loading.active = false;
    }

    /**
     * 
     * @param id ViewConfig列表 配置的id
     * @param payload UI载入数据
     * @param callbacks 打开UI回调
     * @returns uuid 根据prefabpath创建唯一id
     */
    public open(id: number, payload?: any, callbacks?: UICallbacks): string {
        let uuid: string = "";

        let viewConf: ViewConfig = this._viewConfMap.get(id);

        switch (viewConf.type) {
            case ViewType.Arena: {
                break;
            }

            case ViewType.Decoration: {
                break;
            }

            case ViewType.UI: {
                uuid = this.openUI(viewConf, payload, callbacks);
                break;
            }

            case ViewType.PopUp: {
                break;
            }
        }

        return uuid;
    }

    public openUI(viewConf: ViewConfig, payload?: any, callbacks?: UICallbacks): string {

        var uicallback: UICallbacks = callbacks ? callbacks : {
            onAdded: (uuid: string, node: Node, payload: any) => {
                let viewer = null;

                if (payload.viewer) {
                    if (payload.viewer instanceof Array) {
                        type comp_constructor<T = unknown> = new (...args: any[]) => T;

                        let viewers: Array<comp_constructor<Component>> = payload.viewer;
                        viewers.forEach((v) => {
                            let comp = node.getComponent(v)
                            if (!comp)
                                viewer = node.addComponent(v)
                        })
                    } else {
                        let comp = node.getComponent(payload.viewer)
                        if (!comp)
                            viewer = node.addComponent(payload.viewer)
                    }
                }

            }
        };

        let uuid: string = this.ui.openView(viewConf, payload, uicallback);
        return uuid;
    }


    public remove(viewConf: ViewConfig, isDestroy: boolean = true) {
        switch (viewConf.type) {
            case ViewType.Arena: {
                break;
            }

            case ViewType.Decoration: {
                break;
            }

            case ViewType.UI: {
                this.ui.remove(viewConf.prefab, isDestroy);
                break;
            }

            case ViewType.PopUp: {
                break;
            }
            default: {

            }
        }
    }

    public removeByNode(node: Node, isDestroy: boolean = false) {
        if (node instanceof Node) {
            let comp = node.getComponent(UIDelegateComp);
            if (comp && comp.viewParams) {
                this.remove(comp.viewParams.config);
            }
            else {
                Logger.warn(`当前删除的node不是通过界面管理器添加到舞台上`);
                node.destroy();
            }
            return;
        }
    }


    public createUIByID(id: number): Promise<Node> {
        let viewConf: ViewConfig = this._viewConfMap.get(id);
        return this.createView(viewConf.prefab);
    }

    public createUI(prefab: any, callback: Function) {
        if (prefab instanceof Prefab) {
            let ui: Node = instantiate(prefab);
            callback(ui);
        } else {
            this.showLoading();
            app.res.load(prefab, (err: Error | null, res: Prefab) => {
                if (err) {
                    error(err);
                }

                let ui: Node = instantiate(res);
                callback(ui);

                this.hideLoading();
            });
        }
    }

    public async createView(prefabOrPath: Prefab | string, parent?: Node, args?: any): Promise<Node> {
        let node: Node = null;
        let prefab: Prefab = null;
        if (prefabOrPath instanceof Prefab) {
            prefab = prefabOrPath;
        } else {
            prefab = await app.res.loadASync(prefabOrPath, Prefab);
        }

        node = instantiate(prefab);

        if (parent)
            node.parent = parent;

        let delegateComp: UIDelegateComp = node.addComponent(UIDelegateComp);
        delegateComp.viewParams = new ViewParams();
        delegateComp.viewParams.payload = args;
        delegateComp.add();

        return Promise.resolve(node);
    }

    public async createSpineNodeASync(skeFile: string) {
        let skeletonData: sp.SkeletonData = await app.res.loadASync(skeFile, sp.SkeletonData);

        let spineNode = new Node();
        let skeleton = spineNode.addComponent(sp.Skeleton);

        skeleton.skeletonData = skeletonData;
        // skeleton.premultipliedAlpha = true;

        return spineNode;
    }

    public async createSpriteFrameASync(path: string) {
        let sprf = await app.res.loadASync(path + '/spriteFrame', SpriteFrame);
        return sprf;
    }
}